import { reactive, computed, defineComponent, ref, onMounted, PropType } from "vue";
import Day from "./day";
import { getCalendarData } from "./useCalendar";
import { createNamespace, extend } from "../utils";
const [name, bem] = createNamespace("calendar");

export type CalendarType = "single" | "multiple" | "range";
export const baseProps = {
  // 选择类型(single单个日期,multiple多个日期,range日期区间)
  type: {
    type: String as PropType<CalendarType>,
    default: "single",
  },
  // 可选择的最小日期(今天)
  minDate: {
    type: Date,
    default() {
      const now = new Date();
      return new Date(now.getFullYear(), now.getMonth(), now.getDate());
    },
  },
  // 可选择的最大日期(今天往后延6个月)
  maxDate: {
    type: Date,
    default() {
      const now = new Date();
      return new Date(now.getFullYear(), now.getMonth() + 6, now.getDate());
    },
  },
  // 日期区间或多选最多可选天数
  maxRange: {
    type: Number,
    default: 0,
  },
};
export default defineComponent({
  props: extend({}, baseProps, {
    // 当前选中的日期
    selectedDate: [Date, Array] as PropType<Date | Array<Date>>,
  }),
  setup(props) {
    const state = reactive({
      // 日期标题索引
      dateIndex: 0,
      dateVisible: false,
    });

    // 日历展示的数据
    const calendarData = computed(() => {
      return getCalendarData(props.minDate, props.maxDate);
    });

    // body节点
    const bodyRef = ref();

    // 判断滚动位置
    const onScroll = (e: UIEvent) => {
      const target = e.target as HTMLElement;
      const scrollTop = target.scrollTop;
      const bodyNode = bodyRef.value;
      for (let i = 0, l = bodyNode.children.length; i < l; i++) {
        const childNode = bodyNode.children[i];
        if (scrollTop > childNode.offsetTop && scrollTop < childNode.offsetTop + childNode.offsetHeight) {
          changeDateIndex(i);
          return;
        }
      }
    };

    // 修改日期标题索引
    let timerId: any | null;
    const changeDateIndex = (i: number) => {
      if (state.dateIndex === i) return;
      state.dateIndex = i;
      state.dateVisible = true;
      clearTimeout(timerId);
      timerId = setTimeout(() => {
        state.dateVisible = false;
      }, 1000);
    };

    // 获取每一天的类型
    const getDayType = (item: any, day: number) => {
      const timeStamp = +new Date(item.year, item.month, day);
      // 超过可选日期范围禁用
      if (timeStamp < props.minDate.getTime() || timeStamp > props.maxDate.getTime()) {
        return "disabled";
      }
      // 选择数据时的类型
      if (!props.selectedDate) return;
      if (props.type === "single") {
        const _date = props.selectedDate as Date;
        return _date.getTime() === timeStamp ? "selected" : "";
      } else if (props.type === "multiple") {
        const _date = props.selectedDate as Array<Date>;
        for (let i = 0; i < _date.length; i++) {
          if (_date[i].getTime() === timeStamp) {
            return "selected";
          }
        }
        // 选中数量达到最大数其他的都禁用
        if (_date.length >= props.maxRange && props.maxRange != 0) return "disabled";
      } else if (props.type === "range") {
        let res;
        const _date = props.selectedDate as Array<Date>;
        for (let i = 0; i < _date.length; i++) {
          if (_date[i].getTime() === timeStamp) {
            res = i === 0 ? "start" : "end";
          }
        }
        if (_date.length === 2) {
          if (timeStamp > _date[0].getTime() && timeStamp < _date[1].getTime()) {
            return "middle";
          }
        }
        return res;
      }
    };

    // 解决ios-webkit-overflow-scrolling: touch回弹引起的bug
    let lastY = 0;
    const onTouchstart = (e: TouchEvent) => {
      lastY = e.touches[0].clientY;
    };
    const onTouchmove = (e: TouchEvent) => {
      let direction = lastY - e.touches[0].clientY < 0 ? "down" : "up";
      const bodyNode = bodyRef.value;
      // 设置回弹后滑到顶部或者底部再滑一下即可卡死不动，判断滚到顶或低阻止默认行为可以解决卡死bug
      if ((bodyNode.scrollTop === 0 && direction === "down") || (bodyNode.scrollTop === bodyNode.scrollHeight - bodyNode.clientHeight && direction === "up")) {
        e.preventDefault();
      }
    };

    // 挂载时设置初始位置(初始位置定在选中最小日期月份位置上)
    onMounted(() => {
      if (!props.selectedDate) return;
      const miniSort = (selectedDate: any) => {
        // 获得选中的最小日期
        return [].concat(selectedDate).sort(function (a: Date, b: Date) {
          return a.getTime() - b.getTime();
        })[0];
      };
      let minDate = Array.isArray(props.selectedDate) ? miniSort(props.selectedDate) : props.selectedDate;

      // 选中的最小日期年减去可选的最小日期年得到他们的间隔
      let yearInterval = minDate.getFullYear() - (props.minDate ? props.minDate.getFullYear() : 0);
      // 月间隔也一样
      let monthInterval = minDate.getMonth() - (props.minDate ? props.minDate.getMonth() : 0);
      // 年间隔乘12加上月间隔得到月间隔的总数(这个总数刚好和ui的月模块索引值对应上)
      let totalMonths = yearInterval * 12 + monthInterval;
      // 初始位置定义在对应的月模块上
      const bodyNode = bodyRef.value;
      bodyNode.scrollTop = bodyNode.children[totalMonths].offsetTop;
    });

    const renderDays = (item: any) => {
      let res = [];
      for (let i = 1; i <= item.days; i++) {
        res.push(<Day type={getDayType(item, i)} item={item} day={i} key={i} />);
      }
      return res;
    };
    return () => {
      return (
        <div ref={bodyRef} class={bem("body")} onScroll={onScroll} onTouchstart={onTouchstart} onTouchmove={onTouchmove}>
          {calendarData.value.map((item, index) => (
            <div class={bem("month")} key={index}>
              <div class={bem("month-title")}>
                {item.year}年{item.month + 1}月
              </div>
              <div class={bem("days")}>
                <div class={bem("month-mark")}>{item.month + 1}</div>
                {renderDays(item)}
              </div>
            </div>
          ))}
        </div>
      );
    };
  },
});
